#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
舆论监控调度器

负责监控任务的调度、执行和管理。
"""

import asyncio
import logging
import json
import os
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Callable
from dataclasses import dataclass, asdict
from enum import Enum
import sqlite3
from contextlib import asynccontextmanager

from crawlers.douyin.web.enhanced_crawler import EnhancedDouyinCrawler
from .rules import MonitoringRules
from .alerts import AlertManager
from sentiment.keywords import KeywordManager

class MonitorStatus(Enum):
    """监控状态"""
    ACTIVE = "active"
    PAUSED = "paused"
    STOPPED = "stopped"
    ERROR = "error"

@dataclass
class MonitorTarget:
    """监控目标"""
    id: str
    name: str
    target_type: str  # "keyword", "user", "video", "hashtag"
    target_value: str  # 具体的监控内容
    monitor_frequency: int  # 监控频率（分钟）
    max_comments_per_video: int = 50
    include_replies: bool = True
    sentiment_analysis: bool = True
    created_time: str = ""
    last_check: str = ""
    status: str = MonitorStatus.ACTIVE.value
    
    def __post_init__(self):
        if not self.created_time:
            self.created_time = datetime.now().isoformat()

@dataclass
class MonitorResult:
    """监控结果"""
    target_id: str
    timestamp: str
    videos_processed: int
    comments_analyzed: int
    negative_comments: int
    high_risk_comments: int
    overall_sentiment: Dict
    risk_level: str
    alerts_triggered: List[Dict]
    processing_time: float

class SentimentMonitor:
    """舆论监控器"""
    
    def __init__(self, db_path: str = None, config: Dict = None):
        self.logger = logging.getLogger(__name__)
        
        # 配置
        self.config = config or self._get_default_config()
        
        # 数据库路径
        if db_path is None:
            db_path = os.path.join(
                os.path.dirname(__file__), 
                "../data/monitor.db"
            )
        
        self.db_path = db_path
        os.makedirs(os.path.dirname(db_path), exist_ok=True)
        
        # 初始化组件
        self.crawler = EnhancedDouyinCrawler(
            enable_sentiment_analysis=True,
            enable_smart_delay=True
        )
        self.rules = MonitoringRules()
        self.alert_manager = AlertManager()
        self.keyword_manager = KeywordManager()
        
        # 监控目标和状态
        self.targets: Dict[str, MonitorTarget] = {}
        self.monitor_status = MonitorStatus.STOPPED
        self.monitor_tasks: Dict[str, asyncio.Task] = {}
        
        # 初始化数据库
        self._init_database()
        self._load_targets()
        
        self.logger.info("舆论监控器初始化完成")
    
    def _get_default_config(self) -> Dict:
        """获取默认配置"""
        return {
            "max_concurrent_tasks": 3,
            "default_check_interval": 30,  # 分钟
            "max_videos_per_check": 20,
            "alert_cooldown": 300,  # 秒
            "data_retention_days": 30,
            "enable_auto_cleanup": True
        }
    
    def _init_database(self):
        """初始化数据库"""
        with sqlite3.connect(self.db_path) as conn:
            # 监控目标表
            conn.execute("""
                CREATE TABLE IF NOT EXISTS monitor_targets (
                    id TEXT PRIMARY KEY,
                    name TEXT NOT NULL,
                    target_type TEXT NOT NULL,
                    target_value TEXT NOT NULL,
                    monitor_frequency INTEGER NOT NULL,
                    max_comments_per_video INTEGER DEFAULT 50,
                    include_replies BOOLEAN DEFAULT TRUE,
                    sentiment_analysis BOOLEAN DEFAULT TRUE,
                    created_time TEXT NOT NULL,
                    last_check TEXT,
                    status TEXT NOT NULL DEFAULT 'active'
                )
            """)
            
            # 监控结果表
            conn.execute("""
                CREATE TABLE IF NOT EXISTS monitor_results (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    target_id TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    videos_processed INTEGER NOT NULL,
                    comments_analyzed INTEGER NOT NULL,
                    negative_comments INTEGER NOT NULL,
                    high_risk_comments INTEGER NOT NULL,
                    overall_sentiment TEXT NOT NULL,
                    risk_level TEXT NOT NULL,
                    alerts_triggered TEXT NOT NULL,
                    processing_time REAL NOT NULL,
                    FOREIGN KEY (target_id) REFERENCES monitor_targets (id)
                )
            """)
            
            # 告警记录表
            conn.execute("""
                CREATE TABLE IF NOT EXISTS alert_records (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    target_id TEXT NOT NULL,
                    alert_type TEXT NOT NULL,
                    severity TEXT NOT NULL,
                    title TEXT NOT NULL,
                    content TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    resolved BOOLEAN DEFAULT FALSE,
                    FOREIGN KEY (target_id) REFERENCES monitor_targets (id)
                )
            """)
            
            conn.commit()
    
    def add_target(self, target: MonitorTarget) -> bool:
        """添加监控目标"""
        try:
            with sqlite3.connect(self.db_path) as conn:
                target_dict = asdict(target)
                
                conn.execute("""
                    INSERT OR REPLACE INTO monitor_targets 
                    (id, name, target_type, target_value, monitor_frequency,
                     max_comments_per_video, include_replies, sentiment_analysis,
                     created_time, last_check, status)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """, (
                    target.id, target.name, target.target_type, target.target_value,
                    target.monitor_frequency, target.max_comments_per_video,
                    target.include_replies, target.sentiment_analysis,
                    target.created_time, target.last_check, target.status
                ))
                
                conn.commit()
            
            self.targets[target.id] = target
            self.logger.info(f"已添加监控目标: {target.name} ({target.id})")
            
            # 如果监控器正在运行，启动新目标的监控任务
            if self.monitor_status == MonitorStatus.ACTIVE:
                self._start_target_monitoring(target.id)
            
            return True
            
        except Exception as e:
            self.logger.error(f"添加监控目标失败: {e}")
            return False
    
    def remove_target(self, target_id: str) -> bool:
        """移除监控目标"""
        try:
            # 停止监控任务
            if target_id in self.monitor_tasks:
                self.monitor_tasks[target_id].cancel()
                del self.monitor_tasks[target_id]
            
            # 从数据库删除
            with sqlite3.connect(self.db_path) as conn:
                conn.execute("DELETE FROM monitor_targets WHERE id = ?", (target_id,))
                conn.commit()
            
            # 从内存删除
            if target_id in self.targets:
                target_name = self.targets[target_id].name
                del self.targets[target_id]
                self.logger.info(f"已删除监控目标: {target_name} ({target_id})")
            
            return True
            
        except Exception as e:
            self.logger.error(f"删除监控目标失败: {e}")
            return False
    
    def _load_targets(self):
        """从数据库加载监控目标"""
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.row_factory = sqlite3.Row
                cursor = conn.execute("SELECT * FROM monitor_targets")
                
                for row in cursor.fetchall():
                    target = MonitorTarget(
                        id=row['id'],
                        name=row['name'],
                        target_type=row['target_type'],
                        target_value=row['target_value'],
                        monitor_frequency=row['monitor_frequency'],
                        max_comments_per_video=row['max_comments_per_video'],
                        include_replies=bool(row['include_replies']),
                        sentiment_analysis=bool(row['sentiment_analysis']),
                        created_time=row['created_time'],
                        last_check=row['last_check'] or "",
                        status=row['status']
                    )
                    self.targets[target.id] = target
                
                self.logger.info(f"已加载 {len(self.targets)} 个监控目标")
                
        except Exception as e:
            self.logger.error(f"加载监控目标失败: {e}")
    
    async def start_monitoring(self) -> bool:
        """启动监控"""
        if self.monitor_status == MonitorStatus.ACTIVE:
            self.logger.warning("监控器已在运行")
            return True
        
        try:
            self.monitor_status = MonitorStatus.ACTIVE
            
            # 启动所有活跃目标的监控任务
            for target_id, target in self.targets.items():
                if target.status == MonitorStatus.ACTIVE.value:
                    self._start_target_monitoring(target_id)
            
            self.logger.info(f"舆论监控已启动，监控 {len(self.monitor_tasks)} 个目标")
            return True
            
        except Exception as e:
            self.logger.error(f"启动监控失败: {e}")
            self.monitor_status = MonitorStatus.ERROR
            return False
    
    def _start_target_monitoring(self, target_id: str):
        """启动单个目标的监控任务"""
        if target_id not in self.targets:
            return
        
        if target_id in self.monitor_tasks:
            # 如果任务已存在，先取消
            self.monitor_tasks[target_id].cancel()
        
        # 创建新的监控任务
        task = asyncio.create_task(self._monitor_target_loop(target_id))
        self.monitor_tasks[target_id] = task
        
        target_name = self.targets[target_id].name
        self.logger.info(f"已启动监控任务: {target_name} ({target_id})")
    
    async def _monitor_target_loop(self, target_id: str):
        """单个目标的监控循环"""
        target = self.targets[target_id]
        
        while (self.monitor_status == MonitorStatus.ACTIVE and 
               target.status == MonitorStatus.ACTIVE.value):
            try:
                # 执行监控检查
                result = await self._check_target(target)
                
                # 保存结果
                await self._save_monitor_result(result)
                
                # 检查告警规则
                alerts = await self._check_alert_rules(target, result)
                
                # 触发告警
                for alert in alerts:
                    await self.alert_manager.send_alert(alert)
                    await self._save_alert_record(target_id, alert)
                
                # 更新最后检查时间
                target.last_check = datetime.now().isoformat()
                await self._update_target_last_check(target_id, target.last_check)
                
                self.logger.info(
                    f"监控检查完成: {target.name}, "
                    f"处理视频数: {result.videos_processed}, "
                    f"分析评论数: {result.comments_analyzed}, "
                    f"负面评论数: {result.negative_comments}"
                )
                
                # 等待下次检查
                await asyncio.sleep(target.monitor_frequency * 60)
                
            except asyncio.CancelledError:
                self.logger.info(f"监控任务已取消: {target.name}")
                break
            except Exception as e:
                self.logger.error(f"监控目标 {target.name} 时出错: {e}")
                # 短暂延迟后重试
                await asyncio.sleep(60)
    
    async def _check_target(self, target: MonitorTarget) -> MonitorResult:
        """检查单个监控目标"""
        start_time = datetime.now()
        
        if target.target_type == "keyword":
            # 关键词监控
            result = await self._monitor_keyword(target)
        elif target.target_type == "video":
            # 单视频监控
            result = await self._monitor_video(target)
        elif target.target_type == "user":
            # 用户监控
            result = await self._monitor_user(target)
        else:
            raise ValueError(f"不支持的监控类型: {target.target_type}")
        
        # 计算处理时间
        processing_time = (datetime.now() - start_time).total_seconds()
        result.processing_time = processing_time
        
        return result
    
    async def _monitor_keyword(self, target: MonitorTarget) -> MonitorResult:
        """监控关键词"""
        keyword = target.target_value
        
        # 搜索视频
        search_result = await self.crawler.fetch_videos_complete_data(
            keyword, count=min(target.max_comments_per_video, 20)
        )
        
        if not search_result or 'videos' not in search_result:
            return MonitorResult(
                target_id=target.id,
                timestamp=datetime.now().isoformat(),
                videos_processed=0,
                comments_analyzed=0,
                negative_comments=0,
                high_risk_comments=0,
                overall_sentiment={"positive": 0, "negative": 0, "neutral": 0},
                risk_level="low",
                alerts_triggered=[],
                processing_time=0.0
            )
        
        # 分析每个视频的评论
        total_comments = 0
        total_negative = 0
        total_high_risk = 0
        overall_sentiment = {"positive": 0, "negative": 0, "neutral": 0}
        all_alerts = []
        
        videos = search_result.get('videos', [])[:self.config["max_videos_per_check"]]
        
        for video in videos:
            aweme_id = video.get('aweme_id', '')
            if not aweme_id:
                continue
            
            try:
                # 获取增强评论数据
                comment_data = await self.crawler.fetch_enhanced_video_comments(
                    aweme_id,
                    max_comments=target.max_comments_per_video,
                    include_replies=target.include_replies,
                    sentiment_analysis=target.sentiment_analysis
                )
                
                # 统计数据
                sentiment_summary = comment_data.get("sentiment_summary", {})
                if sentiment_summary.get("enabled", False):
                    sentiment_dist = sentiment_summary.get("sentiment_distribution", {})
                    for sentiment, count in sentiment_dist.items():
                        overall_sentiment[sentiment] += count
                    
                    total_comments += sentiment_summary.get("total", 0)
                    total_negative += sentiment_dist.get("negative", 0)
                    total_high_risk += sentiment_summary.get("high_risk_count", 0)
                
            except Exception as e:
                self.logger.error(f"分析视频评论失败 {aweme_id}: {e}")
                continue
        
        # 确定整体风险等级
        if total_comments > 0:
            negative_rate = total_negative / total_comments
            high_risk_rate = total_high_risk / total_comments
            
            if high_risk_rate >= 0.1 or negative_rate >= 0.5:
                risk_level = "critical"
            elif high_risk_rate >= 0.05 or negative_rate >= 0.3:
                risk_level = "high"
            elif negative_rate >= 0.2:
                risk_level = "medium"
            else:
                risk_level = "low"
        else:
            risk_level = "low"
        
        return MonitorResult(
            target_id=target.id,
            timestamp=datetime.now().isoformat(),
            videos_processed=len(videos),
            comments_analyzed=total_comments,
            negative_comments=total_negative,
            high_risk_comments=total_high_risk,
            overall_sentiment=overall_sentiment,
            risk_level=risk_level,
            alerts_triggered=all_alerts,
            processing_time=0.0  # 稍后设置
        )
    
    async def _monitor_video(self, target: MonitorTarget) -> MonitorResult:
        """监控单个视频"""
        video_url = target.target_value
        
        try:
            # 提取视频ID
            from crawlers.douyin.web.utils import AwemeIdFetcher
            aweme_id = AwemeIdFetcher.get_aweme_id(video_url)
            
            if not aweme_id:
                raise ValueError(f"无法提取视频ID: {video_url}")
            
            # 获取增强评论数据
            comment_data = await self.crawler.fetch_enhanced_video_comments(
                aweme_id,
                max_comments=target.max_comments_per_video,
                include_replies=target.include_replies,
                sentiment_analysis=target.sentiment_analysis
            )
            
            # 提取统计信息
            sentiment_summary = comment_data.get("sentiment_summary", {})
            sentiment_dist = sentiment_summary.get("sentiment_distribution", {})
            
            return MonitorResult(
                target_id=target.id,
                timestamp=datetime.now().isoformat(),
                videos_processed=1,
                comments_analyzed=sentiment_summary.get("total", 0),
                negative_comments=sentiment_dist.get("negative", 0),
                high_risk_comments=sentiment_summary.get("high_risk_count", 0),
                overall_sentiment=sentiment_dist,
                risk_level=sentiment_summary.get("risk_level", "low"),
                alerts_triggered=[],
                processing_time=0.0
            )
            
        except Exception as e:
            self.logger.error(f"监控视频失败: {e}")
            raise
    
    async def _monitor_user(self, target: MonitorTarget) -> MonitorResult:
        """监控用户"""
        # 暂时不实现用户监控，返回空结果
        return MonitorResult(
            target_id=target.id,
            timestamp=datetime.now().isoformat(),
            videos_processed=0,
            comments_analyzed=0,
            negative_comments=0,
            high_risk_comments=0,
            overall_sentiment={"positive": 0, "negative": 0, "neutral": 0},
            risk_level="low",
            alerts_triggered=[],
            processing_time=0.0
        )
    
    async def _check_alert_rules(self, target: MonitorTarget, 
                               result: MonitorResult) -> List[Dict]:
        """检查告警规则"""
        alerts = []
        
        # 基础告警规则
        if result.risk_level == "critical":
            alerts.append({
                "type": "high_risk",
                "severity": "critical",
                "title": f"严重舆情风险: {target.name}",
                "content": f"检测到严重负面舆情，负面评论数: {result.negative_comments}，高风险评论数: {result.high_risk_comments}",
                "target_id": target.id,
                "timestamp": result.timestamp
            })
        
        elif result.risk_level == "high":
            alerts.append({
                "type": "medium_risk",
                "severity": "high",
                "title": f"中等舆情风险: {target.name}",
                "content": f"检测到较高负面舆情，负面评论数: {result.negative_comments}，高风险评论数: {result.high_risk_comments}",
                "target_id": target.id,
                "timestamp": result.timestamp
            })
        
        # 负面评论数量告警
        if result.negative_comments >= 20:
            alerts.append({
                "type": "negative_surge",
                "severity": "warning",
                "title": f"负面评论激增: {target.name}",
                "content": f"检测到大量负面评论，总数: {result.negative_comments}",
                "target_id": target.id,
                "timestamp": result.timestamp
            })
        
        # 高风险评论告警
        if result.high_risk_comments >= 5:
            alerts.append({
                "type": "threat_comments",
                "severity": "high",
                "title": f"高威胁评论: {target.name}",
                "content": f"检测到高威胁等级评论，数量: {result.high_risk_comments}",
                "target_id": target.id,
                "timestamp": result.timestamp
            })
        
        return alerts
    
    async def _save_monitor_result(self, result: MonitorResult):
        """保存监控结果"""
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute("""
                    INSERT INTO monitor_results 
                    (target_id, timestamp, videos_processed, comments_analyzed,
                     negative_comments, high_risk_comments, overall_sentiment,
                     risk_level, alerts_triggered, processing_time)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """, (
                    result.target_id, result.timestamp, result.videos_processed,
                    result.comments_analyzed, result.negative_comments,
                    result.high_risk_comments, json.dumps(result.overall_sentiment),
                    result.risk_level, json.dumps(result.alerts_triggered),
                    result.processing_time
                ))
                conn.commit()
        except Exception as e:
            self.logger.error(f"保存监控结果失败: {e}")
    
    async def _save_alert_record(self, target_id: str, alert: Dict):
        """保存告警记录"""
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute("""
                    INSERT INTO alert_records 
                    (target_id, alert_type, severity, title, content, timestamp)
                    VALUES (?, ?, ?, ?, ?, ?)
                """, (
                    target_id, alert["type"], alert["severity"],
                    alert["title"], alert["content"], alert["timestamp"]
                ))
                conn.commit()
        except Exception as e:
            self.logger.error(f"保存告警记录失败: {e}")
    
    async def _update_target_last_check(self, target_id: str, last_check: str):
        """更新目标最后检查时间"""
        try:
            with sqlite3.connect(self.db_path) as conn:
                conn.execute(
                    "UPDATE monitor_targets SET last_check = ? WHERE id = ?",
                    (last_check, target_id)
                )
                conn.commit()
        except Exception as e:
            self.logger.error(f"更新最后检查时间失败: {e}")
    
    async def stop_monitoring(self):
        """停止监控"""
        self.monitor_status = MonitorStatus.STOPPED
        
        # 取消所有监控任务
        for task in self.monitor_tasks.values():
            task.cancel()
        
        # 等待任务完成
        if self.monitor_tasks:
            await asyncio.gather(*self.monitor_tasks.values(), return_exceptions=True)
        
        self.monitor_tasks.clear()
        self.logger.info("舆论监控已停止")
    
    def get_targets(self) -> List[Dict]:
        """获取所有监控目标"""
        return [asdict(target) for target in self.targets.values()]
    
    def get_target(self, target_id: str) -> Optional[Dict]:
        """获取指定监控目标"""
        if target_id in self.targets:
            return asdict(self.targets[target_id])
        return None
    
    def get_monitor_status(self) -> Dict:
        """获取监控状态"""
        return {
            "status": self.monitor_status.value,
            "total_targets": len(self.targets),
            "active_targets": len([t for t in self.targets.values() 
                                 if t.status == MonitorStatus.ACTIVE.value]),
            "running_tasks": len(self.monitor_tasks),
            "config": self.config
        }